package cn.lzm.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.lzm.serializer.Serializer;
import lombok.SneakyThrows;

import javax.management.relation.RelationNotFoundException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lzm
 * @Date 2024/8/4
 * @description
 */
public class SpiLoader {

    private static final List<String> SCAN_DIRS = ListUtil.of("META-INF/rpc/system/", "META-INF/rpc/custom/");

    private static final Map<Class<?>, Map<String, Class<?>>> LOADER_MAP = new ConcurrentHashMap<>();

    private static final Map<String, Object> INSTANCE_MAP = new ConcurrentHashMap<>();

    /**
     * 将SPI中的配置转为map放到内存中，这一步会在项目启动的时候初始化的过程中就会做这件事，后续根据这个map
     * 来创建实例，比如序列化的实例，注册中心的实例
     *
     * @param loadClass SPI配置的接口的全限定路径，根据这个路径可以找到相应的配置文件
     */
    @SneakyThrows
    public static void load(Class<?> loadClass) {
        System.out.printf("开始加载类型为%s的SPI\n", Serializer.class.getName());
        HashMap<String, Class<?>> configMap = new HashMap<>();
        for (String scanDir : SCAN_DIRS) {
            List<URL> resources = ResourceUtil.getResources(scanDir + loadClass.getName());
            for (URL resource : resources) {
                FileReader fr = new FileReader(resource.getPath());
                List<String> configs = fr.readLines();
                for (String config : configs) {
                    String[] conf = config.split("=");
                    configMap.put(conf[0], Class.forName(conf[1]));
                }
            }
        }
        LOADER_MAP.put(loadClass, configMap);
    }

    /**
     * 获取配置实例
     *
     * @param clazz 配置类型
     * @param key   配置的名称
     * @param <T>   配置类型
     * @return 配置
     */
    @SneakyThrows
    @SuppressWarnings("ALL")
    public static <T> T getInstance(Class<T> clazz, String key) {
        Map<String, Class<?>> config = LOADER_MAP.get(clazz);
        if (CollectionUtil.isEmpty(config)) {
            throw new RuntimeException(String.format("SPI未加载%s类型的配置", clazz.getName()));
        }
        Class<?> implClazz = config.get(key);
        if (Objects.isNull(implClazz)) {
            throw new RelationNotFoundException(String.format("SPI的%s类型中不存在key=%s的配置", clazz.getName(), key));
        }
        String clazzName = implClazz.getName();
        if (!INSTANCE_MAP.containsKey(clazzName)) {
            // 将实例对象缓存起来
            INSTANCE_MAP.put(clazzName, implClazz.newInstance());
        }
        return (T) INSTANCE_MAP.get(clazzName);
    }

    public static void main(String[] args) {
        load(Serializer.class);
        Serializer serializer = getInstance(Serializer.class, "4");
        System.out.println(serializer);
    }

}